package org.example.config;

import com.alibaba.fastjson.JSONObject;
import org.example.entity.RestBean;
import org.example.service.AuthorizeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;

@Configuration  //标记该类为 Spring 的配置类，Spring 会自动扫描并加载该类中的配置信息
@EnableWebSecurity //启用 Spring Security 的 Web 安全支持，会自动创建一个 Spring Security 的过滤器链
public class SecurityConfiguration {
    private static final Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
    @Resource
    AuthorizeService authorizeService;

    @Resource
    DataSource dataSource;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.addFilterBefore((request, response, chain) -> {
            logger.info("请求路径: {}", request.getParameterNames());
            chain.doFilter(request, response);
        }, UsernamePasswordAuthenticationFilter.class);

        return  http
                .authorizeHttpRequests()  //开启对 HTTP 请求的授权配置
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()  //对所有的 HTTP 请求都要求进行身份验证
                .and()
                .formLogin()  //启用表单登录功能。用户可以通过一个 HTML 表单来输入用户名和密码进行登录
                .loginProcessingUrl("/api/auth/login")  //指定处理登录请求的 URL。用户在登录表单中提交用户名和密码时，表单数据会被发送到这个 URL 进行处理
                .successHandler(this::onAuthenticationSuccess)
                .failureHandler(this::onAuthenticationFailure)
                .and()
                .logout()
                .logoutUrl("/api/auth/logout")
                .logoutSuccessHandler(this::onAuthenticationSuccess)
                .and()
//                .userDetailsService(authorizeService)
                .rememberMe()
                .rememberMeParameter("remember")
                .tokenRepository(this.tokenRepository())
                .tokenValiditySeconds(3600*24*3)
                .and()
                .csrf()  // CSRF（跨站请求伪造）保护
                .disable()  //禁用 CSRF 保护。在某些情况下，如前后端分离的应用中，可能需要禁用 CSRF 保护，因为前端应用可能无法正确处理 CSRF 令牌。
//                .formLogin(Customizer.withDefaults())//生成登录退出页
//                .httpBasic(Customizer.withDefaults())
                .cors()
                .configurationSource(this.corsConfigurationSource())//配置 跨域资源共享（CORS）
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(this::onAuthenticationFailure)
                .and()
                .build();
    }

    /**
     * 跨域处理
     * @return
     */
    private CorsConfigurationSource corsConfigurationSource() {
        // 创建一个 CorsConfiguration 对象，用于配置 CORS 规则
        CorsConfiguration cors = new CorsConfiguration();
        // 允许所有域名访问（或指定具体域名）
        cors.addAllowedOriginPattern("*");
        // 允许携带凭证（如 Cookie、Authorization 头等）
        cors.setAllowCredentials(true);
        // 允许所有请求头
        cors.addAllowedHeader("*");
        // 允许所有 HTTP 方法（GET、POST、PUT、DELETE 等）
        cors.addAllowedMethod("*");
        // 允许暴露所有响应头
        cors.addExposedHeader("*");
        // 创建一个基于 URL 的 CorsConfigurationSource
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        // 将 CORS 配置应用到所有路径（/** 表示所有路径）
        source.registerCorsConfiguration("/**", cors);
        // 返回配置好的 CorsConfigurationSource
        return source;
    }

    @Bean
    public PersistentTokenRepository tokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }

    @Bean
    public AuthenticationManager authenticationManager(HttpSecurity security) throws Exception {
       return security
               .getSharedObject(AuthenticationManagerBuilder.class)
                .userDetailsService(authorizeService)
               .passwordEncoder(passwordEncoder())//关联密码编译器
                .and()
                .build();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return  new BCryptPasswordEncoder();
    }

    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        response.setCharacterEncoding("utf-8");
        if(request.getRequestURI().endsWith("/login"))
            response.getWriter().write(JSONObject.toJSONString(RestBean.success("登录成功")));
        else if(request.getRequestURI().endsWith("/logout"))
            response.getWriter().write(JSONObject.toJSONString(RestBean.success("退出登录成功")));
    }

    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(JSONObject.toJSONString(RestBean.failure(401,exception.getMessage())));//使用fastjson将信息转为json格式
    }
}
